<?php
namespace DRY\Module;

/**
 * DonReY Framework 2012 :: Translation (Multilanguage) module
 * Uses a database to store strings (or references to files for large strings)
 * Features -(planned) !(done) ~(partial):
 * - String groups (Auto-created when adding strings, Garbage collector for groups with no strings)
 * - Strings in various languages (Add, Search ID for string (only for project development, not indexed), Return string by Selector)
 * - Current language stored using Persistence
 * - Defining stored group-sets, and loading them automatically for current language (for example, associate a set of groups to each page type)
 * - Support long-string files (loaded only on demand)
 *
 * @author Wavetrex <wavetrex@gmail.com>
 * @link http://www.donrey.net/Documentation/Structure/Modules/translate.html
 * @license [GPL v3] http://www.gnu.org/licenses/gpl-3.0.html
 * @version 1.0.dev
 * @package DonReY
 * @subpackage Module\Translate
 */
class Translate extends base
{
	// Exception/Error codes
	const
		EXC_UNKNOWN = 0
		;

	/**
	 * Currently instanced group objects - Used to access existing translations directly, no matter in which group they are
	 * @var array[\DRY\Translate\Group]
	 */
	protected $_groups;

	/**
	 * Session container
	 * \DRY\Persistence\Container_base;
	 * @var unknown_type
	 */
	protected $_sc;

	public function __construct($alias, $config)
	{
		parent::__construct($alias, $config);

		// session keeping current user's language
		$this->_sc = $this[(string)$this-> CFG-> session-> storage]-> factory_Container((string)$this-> CFG-> session-> container, (string)$this-> CFG-> session-> container['type']?:'session');

		// set default language (at first run)
		if(!isset($this->_sc-> current_language))
			$this->_sc-> current_language = isset($this-> CFG-> default_language)?(string)$this-> CFG-> default_language:'en';
	}

	/**
	 * Sets the current active language (change default or persistent stored language)
	 * @param string $lang_iso2
	 */
	public function set_Language($lang_iso2)
	{
		$this->_sc-> current_language = $lang_iso2;
	}

	/**
	 * Returns the current active language
	 * @return string
	 */
	public function get_Language()
	{
		return $this->_sc-> current_language;
	}

	/**
	 * Instances a Translation_Group object, optionally preloading it from the database
	 * @param string $group_id
	 * @param bool $preload If true, read all strings for the specified language and group_id at instantiation time
	 * @param string $lang Language to use (if missing, it's using the current_language)
	 */
	public function &factory_Group($group_id, $preload = true, $lang = null)
	{
		// if the group already exists, use it
		if(isset($this->_groups[$group_id]))
			return $this->_groups[$group_id];

		$this->_groups[$group_id] = new \DRY\Translate\Group(is_null($lang)?$this->_sc-> current_language:$lang, $group_id, $this, $preload);
		return $this->_groups[$group_id];
	}

	public function __isset($selector) {
		$r = $this->__get($selector);
		return !empty($r);
	}

	/**
	 * Searches all the groups for a selector, and returns the associated string directly
	 * @param string $selector
	 */
	public function &__get($selector)
	{
		// first, try if selector is actually a group name, then return the group object
		if(isset($this->_groups[$selector]))
			return $this->_groups[$selector];
		// nope, attempt to find the actual selector in all groups
		foreach($this->_groups as &$group) {
			if(isset($group[$selector])) {
				$string = $group[$selector];
				return $string;
			}
		}
		// when not found anywhere, just return empty string
		$string = '';
		return $string;
	}

}

namespace DRY\Translate;

/**
 * Contains translation strings (or references to translation files, which are only loaded on demand)
 * Allows accessing the strings by their selectors, using ArrayAccess or Object properties
 *
 * @author Wavetrex <wavetrex@gmail.com>
 * @link http://www.donrey.net/Documentation/Structure/Modules/translate.html
 * @license [GPL v3] http://www.gnu.org/licenses/gpl-3.0.html
 * @version 1.0.dev
 * @package DonReY
 * @subpackage Module\Translate
 */
class Group implements \ArrayAccess
{
	/**
	 * Group language (usually current language)
	 * @var string
	 */
	private $_lang;

	/**
	 * This group's ID
	 * @var string
	 */
	private $_group_id;

	/**
	 * The strings in this group
	 * @var array
	 */
	private $_strings = array();

	/**
	 * Database connection module
	 * @var \DRY\Module\Database
	 */
	private $_db;

	/**
	 * Queryfactory module
	 * @var \DRY\Module\Queryfactory
	 */
	private $_qf;

	private $_tables = array();

	/**
	 * Initializes the strings group
	 * @param string $lang
	 * @param string $group_id
	 * @param Translate $translate_module The instanced \DRY\Module\Translate which created this group
	 * @param bool $preload If true, loads strings in the group at instance time, if false, loads them only first use (Note: in this case, isset($selector) is not working until strings have been read first time!)
	 */
	public function __construct($lang, $group_id, \DRY\Module\Translate $translate_module, $preload = true)
	{
		$this->_db = $translate_module['connection'];
		$this->_qf = $translate_module['queryfactory'];
		$this->_lang = $lang;
		$this->_group_id = $group_id;

		$this->_tables = array(
			'strings'=> (string)$translate_module-> CFG-> storage-> strings,
			'selectors'=> (string)$translate_module-> CFG-> storage-> selectors
		);

		if($preload) {
			$this-> load_Strings();
		}
	}

	protected function load_Strings()
	{
		$q = $this->_qf-> select()
			-> table($this->_tables['selectors'], 'id,selector')
			-> table($this->_tables['strings'], 'string', 'left', '#2.string_id = #1.id')
			-> where('#1.group = ?', $this->_group_id)
			-> where('#2.lang_iso2 = ?', $this->_lang);
		$r = $this->_db-> query($q);
		foreach($r as $row) {
			$this->_strings[$row['selector']] = $row['string'];
		}
	}

	/**
	 * Checks if a module on which this one depends has been instanced
	 * @param string $selector
	 */
	public function offsetExists($selector)
	{
		return isset($this->_strings[$selector]);
	}

	/**
	 * Object version of the selector checker
	 * @param string $selector
	 */
	public function __isset($selector)
	{
		return $this-> offsetExists($selector);
	}

	/**
	 * Reads a preloaded string in this group, or on-demand loads the linked translation file (for large ones)
	 * @param string $selector
	 */
	public function offsetGet($selector)
	{
		if(empty($this->_strings))
			$this-> load_Strings();
		return isset($this->_strings[$selector])?$this->_strings[$selector]:'';
	}

	/**
	 * Object version of the string reader
	 * @param string $selector
	 */
	public function __get($selector)
	{
		return $this-> offsetGet($selector);
	}

	/**
	 * Allows updating a string in the database
	 *
	 * @param string $selector
	 * @param string $new_string
	 */
	public function offsetSet($selector, $new_string)
	{
		// not yet
	}

	/**
	 * Object version of the string writer
	 * @param string $selector
	 */
	public function __set($selector, $new_string)
	{
		$this-> offsetSet($selector, $new_string);
	}

	/**
	 * Removes a translation string for the current language and
	 *
	 * @param string $selector
	 */
	public function offsetUnset($selector)
	{
		// not yet
	}

	/**
	 * Object version of the string remover
	 * @param string $selector
	 */
	public function __unset($selector)
	{
		$this-> offsetUnset($selector);
	}

}